Open
Conversation
935fcb9 to
acda857
Compare
6049468 to
c9496cb
Compare
0360313 to
a70f53b
Compare
79d34a3 to
4637505
Compare
4637505 to
b4cdaea
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
2.1.13→3.0.2Release Notes
unjs/unhead (@unhead/vue)
v3.0.2Compare Source
🐞 Bug Fixes
View changes on GitHub
v3.0.1Compare Source
🐞 Bug Fixes
importmap&speculationruleshandling - by @harlan-zw in #734 (8027f)View changes on GitHub
v3.0.0Compare Source
Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.
📣 Highlights
🌊 Streaming SSR
Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new
<title>,<meta>, and<link>tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.Under the hood: a queue stub (
window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #537.
🛠️ Unified Vite Plugin + DevTools
A single
@unhead/{framework}/viteplugin replaces the old manual composition of@unhead/addons+ streaming plugin + framework glue. One import, one call, and you get tree-shaking,useSeoMeta→useHeadtransform, inline minification, streaming SSR, dev-modeValidatePluginauto-injection, and Vite DevTools integration.The DevTools panel surfaces live head state during development: every
useHead()/useSeoMeta()call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph),useScript()load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.Available for Vue, React, Svelte, Solid, and vanilla via
@unhead/bundler/vite(the renamed@unhead/addonspackage; the old name still works with a deprecation warning).See PRs #726, #733, #731.
🎯
useHead()Type NarrowinguseHead()now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.See PRs #627, #665, #729.
✅ ValidatePlugin
New optional
ValidatePluginthat inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late<meta charset>, too manyfetchpriority="high"hints, preconnect withoutcrossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children,hid/vmid,body: true), missingTemplateParamsPlugin, and missingAliasSortingPlugin— all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:See PRs #690, #691, #716, #722, #725, #732.
🔗 Canonical Plugin
New built-in
CanonicalPluginthat auto-generates<link rel="canonical">tags and resolves relative URLs to absolute inog:image,twitter:image, andog:url. Includes query parameter filtering (strips tracking params likeutm_source,fbclid,gclidby default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.See PRs #492, #713.
🗜️ MinifyPlugin
New optional
MinifyPluginthat minifies inline<script>and<style>tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransformin@unhead/bundler) pre-minifies staticinnerHTMLliterals at compile time. Standalone utilities (minifyJS,minifyCSS,minifyJSON) are also available viaunhead/minify.See PR #705.
📦 Performance
Key optimizations:
estree-walker/acorn-loosetooxc-walker(#663)transformHtmlTemplate(#581)TemplateParamsPluginandAliasSortingPluginmade opt-in for smaller bundles (#493, #494)📊 Schema.org
Dataset,MusicAlbum,MusicGroup,MusicPlaylist,MusicRecording,PodcastEpisode,PodcastSeason,PodcastSeries,Service,TVEpisode,TVSeason,TVSeries(#612)ohashanddefudependencies (#605)🔄 Other Changes
renderDOMHead()/renderSSRHead()are now fully synchronous, single-pass via a composableresolveTags()pipeline; the head instance exposes a pluggablerender()function for framework integrations (#619, #622, #628, #629, #630)@unhead/react/helmetdrop-in compat export for users migrating fromreact-helmet(#719)useHeadSafe()now whitelists CSS styles (#491)blockingattribute on scripts and stylesheets (#489)useScript()consolidated back into core, legacy support dropped (#498)fediverse:creatormeta tag support (#703)hookableto lighterHookableCorewith sync-only hooks (#631)@unhead/schema,@unhead/shared) (#678)templateParamsextensible via module augmentation (#679)twitter:cardinInferSeoMetaPlugin(#681)asattribute for preload links (#683)onRenderedcallback option onuseHead()for synchronizing with DOM head updates (#712)tagWeightoption oncreateHead()to override default CAPO tag weight function (#716)🐛 Bug Fixes
<link rel="alternate">correctly (#655, #656, #658)@unhead/vue/stream/iifewith correct types (#707)nullto opt out of default values (#680)targetto array before mergingpotentialAction(#709)titleTemplatetag inresolveTitleTemplate(#715)Synchronous rendering
renderDOMHead()andrenderSSRHead()no longer return promises. Removeawait.Build plugins:
@unhead/addons→@unhead/bundler(#726, #733)The
@unhead/addonspackage has been renamed to@unhead/bundler(the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnheadexport and ship from each framework's/vitesubpath:Strict
Link/Script/Metatypes (#729)LinkandScriptunions no longer fall back toGenericLink/GenericScript, so the type system enforces per-tag constraints (e.g.preload + as: 'font'requirescrossorigin). Metacontentis now required; usecontent: nullexplicitly to remove a meta tag. Customrel/typevalues needsatisfies GenericLink/satisfies GenericScript.Dropped deprecations (#624)
childreninnerHTMLhid/vmidkeybody: truetagPosition: 'bodyClose'useServerHead/useServerSeoMetauseHead/useSeoMetacreateHeadCorecreateUnhead@unhead/vue/legacy@unhead/vue/clientor@unhead/vue/server(legacy path still works with deprecation warning)modeoption on entriescreateHeadimportsCJS removed (#482)
All packages are ESM-only.
Plugins now opt-in
TemplateParamsPluginandAliasSortingPluginare no longer included by default. Import and register them explicitly if needed.Hooks removed
inithook removeddom:renderTag,dom:renderedhooks deprecated (will be removed in v4)dom:beforeRenderis now synchronous (no async handlers)Type changes
HeadHeadTagMetaFlatInputMetaFlatRuntimeMode@unhead/schemaunhead/types@unhead/sharedunheadSchema.org
PluginSchemaOrg/SchemaOrgUnheadPluginreplaced withUnheadSchemaOrgcanonicalHostreplaced withhost,canonicalUrlreplaced withhost+path🔧 Migration Tooling
Add
ValidatePluginduring your upgrade to automatically detect v2 patterns:It will warn about missing plugins, deprecated properties, and other common migration issues. Remove it once migration is complete. If you use the unified Vite plugin,
ValidatePluginis auto-injected in dev so you don't have to wire it up manually. See PRs #722, #733.📖 Migration Guide
See the full Migration Guide for detailed upgrade instructions.
Changelog
🚨 Breaking Changes
useHead()type narrowing - by @harlan-zw in #627 (e8086)renderDOMHead()- by @harlan-zw in #628 (627eb)renderSSRHead()- by @harlan-zw in #629 (44d71)render()function - by @harlan-zw in #630 (baaa7)resolveTags()- by @harlan-zw in #622 (b6a52)🚀 Features
onRendered- by @harlan-zw and Claude Opus 4.6 (1M context) in #712 (55166)MinifyPluginfor inline script/style minification - by @harlan-zw and Claude Opus 4.6 (1M context) in #705 (f1f5d)@unhead/bundlerand/vitesubpath exports - by @harlan-zw in #726 (df9c8)tagWeightoption andmeta-beyond-1mbvalidation rule - by @harlan-zw and Claude Opus 4.6 (1M context) in #716 (c58be)🐞 Bug Fixes
HookableCore- by @harlan-zw in #631 (cc794)transformHtmlTemplate- by @harlan-zw (f696d)fediverse:creatormeta tag support - by @harlan-zw in #703 (545c9)targetto array before merging potentialAction - by @harlan-zw and Claude Opus 4.6 (1M context) in #709 (125ef)book:release_datemeta - by @harlan-zw (e413c)@unhead/vue/stream/iifewith correct types - by @harlan-zw in #707 (a8f1a)🏎 Performance
View changes on GitHub
Configuration
📅 Schedule: (in timezone Europe/Warsaw)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.